//#include "stdafx.h"
#include "filepath.h"
#include <string.h>
#include <stdlib.h>
#include <direct.h>
#include <io.h>
#include <sstream>
#include <ostream>
#include <direct.h>

const char kPathSeparator = '\\';
const char kAlternatePathSeparator = '/';
const char kAlternatePathSeparatorString[] = "/";
const char kCurrentDirectoryString[] = ".\\";
# define GTEST_PATH_MAX_ _MAX_PATH

//----------------------------------------------------------

static bool is_path_separator(char c) {
    return c == kPathSeparator;
}

bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) {
    if (lhs == nullptr) return rhs == nullptr;
    if (rhs == nullptr) return false;
    return _stricmp(lhs, rhs) == 0;
}

bool EndsWithCaseInsensitive(const std::string& str, const std::string& suffix) {
    const size_t str_len = str.length();
    const size_t suffix_len = suffix.length();
    return (str_len >= suffix_len) &&
        CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len,
                                     suffix.c_str());
}



filepath::filepath() : _PathName("") {

}

filepath::filepath(const filepath& rhs) : _PathName(rhs._PathName) {

}

filepath::filepath(const std::string& pathname) : _PathName(pathname) {
    normalize();
}

void filepath::set(const filepath& rhs) {
    _PathName = rhs._PathName;
}

void filepath::get_directory_list(std::vector<std::string>& vec_directory) {
    vec_directory.clear();

    std::string key(&kPathSeparator);

    auto scrLength = _PathName.length() + 1;
    char* pStr = new char[scrLength];
    strcpy_s(pStr, scrLength, _PathName.c_str());

    auto keyLength = key.length() + 1;
    char* pKey = new char[keyLength];
    strcpy_s(pKey, keyLength, key.c_str());

    char* pTmp = NULL;
    char* p = strtok_s(pStr, pKey, &pTmp);
    while (p) {
        std::string s = p;
        int pos = 0;
        if (s.find(".", pos) == std::string::npos) {
            vec_directory.push_back(s);
        }

        p = strtok_s(NULL, pKey, &pTmp);
    }

    delete pStr;
    delete pKey;
}

bool filepath::file_or_directory_exists() const {
    struct _stat file_stat;
    return (_stat(_PathName.c_str(), &file_stat) == 0);
}

bool filepath::is_directory() const {
    return !_PathName.empty() &&
        is_path_separator(_PathName.c_str()[_PathName.length() - 1]);
}

bool filepath::is_root_directory() const {
    return _PathName.length() == 3 && is_absolute_path();
}

bool filepath::is_absolute_path() const {
    const char* const name = _PathName.c_str();

    return _PathName.length() >= 3 &&
        ((name[0] >= 'a' && name[0] <= 'z') ||
         (name[0] >= 'A' && name[0] <= 'Z')) &&
        name[1] == ':' &&
        is_path_separator(name[2]);
}

filepath filepath::remove_trailing_path_separator() const {
    return is_directory()
        ? filepath(_PathName.substr(0, _PathName.length() - 1))
        : *this;
}

filepath filepath::remove_directory_name() const {
    const char* const last_sep = find_last_path_separator();
    return last_sep ? filepath(last_sep + 1) : *this;
}

filepath filepath::remove_filename() const {
    const char* const last_sep = find_last_path_separator();
    std::string dir;
    if (last_sep) {
        dir = std::string(c_str(), static_cast<size_t>(last_sep + 1 - c_str()));
    } else {
        dir = kCurrentDirectoryString;
    }
    return filepath(dir);
}

filepath filepath::remove_etension(const char* extension) const {
    const std::string dot_extension = std::string(".") + extension;
    if (EndsWithCaseInsensitive(_PathName, dot_extension)) {
        return filepath(_PathName.substr(
            0, _PathName.length() - dot_extension.length()));
    }
    return *this;
}

bool filepath::remove_directory_or_file(const filepath& path) {
    if (path.string().length() <= 0)
        return false;

    struct _stat file_stat;
    if (_stat(path.c_str(), &file_stat) != 0)
        return false;

    if ((S_IFREG & file_stat.st_mode) != 0) {
        remove(path.c_str());
        return true;
    }

    intptr_t hFile = 0;
    struct _finddata_t fileinfo;
    std::string tempStr;
    std::string p;
    int32_t pos = -1;
    if ((hFile = _findfirst(p.assign(path.string()).append("\\*").c_str(), &fileinfo)) != -1) {
        do {
            if ((strcmp(fileinfo.name, ".") == 0) || (strcmp(fileinfo.name, "..") == 0))
                continue;

            filepath newPath(path.string() + kPathSeparator + fileinfo.name);
            if ((fileinfo.attrib & _A_SUBDIR))//琌戈Ж
            {
                remove_directory_or_file(newPath);
                _rmdir(newPath.c_str());
            } else if ((fileinfo.attrib & _A_ARCH)) //戈
            {
                remove(newPath.c_str());
            }

        } while (_findnext(hFile, &fileinfo) == 0);
        _findclose(hFile);
    }// if

    if ((S_IFDIR & file_stat.st_mode) != 0) {
        _rmdir(path.c_str());
        return true;
    }

    return false;
}

bool filepath::create_directories_recursively() const {
    if (!this->is_directory()) {
        return false;
    }

    if (_PathName.length() == 0 || this->directory_exists()) {
        return true;
    }

    const filepath parent(this->remove_trailing_path_separator().remove_filename());
    return parent.create_directories_recursively() && this->create_folder();
}

bool filepath::create_folder() const {
    int result = _mkdir(_PathName.c_str());

    if (result == -1) {
        return this->directory_exists();
    }
    return true;
}

bool filepath::directory_exists() const {
    bool result = false;

    const filepath& path(is_root_directory() ? *this :
                         remove_trailing_path_separator());

    struct _stat file_stat;
    result = (_stat(path.c_str(), &file_stat) == 0) &&
        ((_S_IFDIR & file_stat.st_mode) != 0);

    return result;
}

filepath filepath::get_current_dir() {
    char cwd[GTEST_PATH_MAX_ + 1] = {'\0'};
    return filepath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd);
}

filepath filepath::make_file_name(const filepath& directory, const filepath& base_name, int32_t number, const char* extension) {
    std::string file;
    if (number == 0) {
        file = base_name.string() + "." + extension;
    } else {
        std::stringstream stream;
        stream << number;

        file = base_name.string() + "_" + stream.str() + "." + extension;
    }
    return concat_paths(directory, filepath(file));
}

filepath filepath::concat_paths(const filepath& directory, const filepath& relative_path) {
    if (directory.empty())
        return relative_path;
    const filepath dir(directory.remove_trailing_path_separator());
    return filepath(dir.string() + kPathSeparator + relative_path.string());
}

filepath filepath::generate_unique_file_name(const filepath& directory, const filepath& base_name, const char* extension) {
    filepath full_pathname;
    int32_t number = 0;
    do {
        full_pathname.set(make_file_name(directory, base_name, number++, extension));
    } while (full_pathname.file_or_directory_exists());
    return full_pathname;
}

bool filepath::read_file(const filepath& path, const std::string& file_name, std::string& content, ProcessFuncCB func) {
    try {
        std::string fullpath = path.string() + '\\' + file_name;
        std::ifstream inFile(fullpath);
        std::string line;
        int32_t index = 0;
        bool bSucess = false;
        while (getline(inFile, line)) {
            bSucess = true;
            content.append(line);
            content.append("\n");

            if (func != nullptr)
                func(line);

            ++index;
        }

        return bSucess;
    } catch (...) {
        printf("CFileTool::readFile %s is failed\n", file_name.c_str());
        return false;
    }//try
}

bool filepath::create_file(const filepath& path, const std::string& file_name, const std::string& content, int32_t mode) {
    if (_access(path.c_str(), 0) != 0) {
        if (_mkdir(path.c_str()) != 0) {
            printf("mkdir: %s is fail\n", path.c_str());
            return false;
        }
    }

    std::string fullpath = path.string() + '\\' + file_name;
    std::fstream file(fullpath, std::fstream::out | mode);
    if (file.is_open()) {
        file << content;
    } else {
        printf("open %s is fail.\n", fullpath.c_str());
        file.close();
        return false;
    }

    file.close();
    printf("generate %s success\n", fullpath.c_str());
    return true;
}

void filepath::get_file_list(const filepath& path, std::vector<std::string>& file_list, const std::string& extension) {
    intptr_t hFile = 0;
    struct _finddata_t fileinfo;
    std::string tempStr;
    std::string p;
    std::size_t pos = -1;
    if ((hFile = _findfirst(p.assign(path.string()).append("\\*").c_str(), &fileinfo)) != -1) {
        do {
            if ((fileinfo.attrib & _A_SUBDIR))//琌戈Ж
            {

            } else//獶戈Ж
            {
                tempStr.assign(fileinfo.name);

                if (extension.length() > 0) {
                    pos = tempStr.find(extension);
                    if (pos < 0)
                        continue;

                    file_list.push_back(tempStr);
                } else {
                    file_list.push_back(tempStr);
                }
            }
        } while (_findnext(hFile, &fileinfo) == 0);
        _findclose(hFile);
    }// if
}

void filepath::normalize() {
    if (_PathName.c_str() == nullptr) {
        _PathName = "";
        return;
    }
    const char* src = _PathName.c_str();
    char* const dest = new char[_PathName.length() + 1];
    char* dest_ptr = dest;
    memset(dest_ptr, 0, _PathName.length() + 1);

    while (*src != '\0') {
        *dest_ptr = *src;
        if (!is_path_separator(*src)) {
            src++;
        } else {
            while (is_path_separator(*src))
                src++;
        }
        dest_ptr++;
    }
    *dest_ptr = '\0';
    _PathName = dest;
    delete[] dest;
}

const char* filepath::find_last_path_separator() const {
    const char* const last_sep = strrchr(c_str(), kPathSeparator);

    return last_sep;
}
